#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys, time, datetime, random, binascii, hashlib, sqlite3, json, version, re, math, inspect
import languages

class custom_exception(Exception):
	pass

# Translate function, needs to be declared early as it is needed in initialization	

class fake_r():
	session = {"language": "zh_Hans"}

r = fake_r()

def t(word, arguments = (), generic = False):
	if r.session['language'] == "en":
		return word % arguments

	if generic:
		if global_vars["environment"] == "dev":
			sql_con = sqlite3.connect("translations.db")
			sql_cur = sql_con.cursor()
			try:
				result = sql_cur.execute("select translation from "+r.session['language']+" where file = -1 and function = -1 and word = ?", (word,)).fetchone()
				return result[0] % arguments
			except:
				sql_cur.execute("INSERT or IGNORE INTO "+r.session['language']+" ('file', 'function', 'word', 'translation') VALUES (-1, -1, ?, ?)", (word, word))
				sql_con.commit()
				return word % arguments
		else:
			try:
				return getattr(languages, r.session['language'])[0]["generic"][word] % arguments
			except:
				return word % arguments
	
	else:
		dir = inspect.stack()[0][1].rsplit('shared_resources.py', 1)[0]
		file = inspect.stack()[1][1].rsplit(dir, 1)[1]
		file = file.replace("\\", "/")
		
		function = inspect.stack()[1][3]
		
		if global_vars["environment"] == "dev":
			sql_con = sqlite3.connect("translations.db")
			sql_cur = sql_con.cursor()
			try:
				result = sql_cur.execute("select translation from "+r.session['language']+" where file = ? and function = ? and word = ?", (file, function, word)).fetchone()
				return result[0] % arguments
			except:
				sql_cur.execute("INSERT or IGNORE INTO "+r.session['language']+" ('file', 'function', 'word', 'translation') VALUES (?, ?, ?, ?)", (file, function, word, word))
				sql_con.commit()
				return word % arguments
		else:
			try:
				return getattr(languages, r.session['language'])[0][file][function][word] % arguments
			except:
				return word % arguments
	
global_vars = {
	"url": "https://qidi-edu.com",
	"environment": version.get_version(),
	"residual_week": 345600 + -8 * 60 * 60,
	"residual_day": -8 * 60 * 60,
	"lesson_max_time": 60 * 60 * 5,
	"mail_smtp_host": 'smtp.exmail.qq.com',
	"mail_smtp_user": 'info@qidi-edu.com',
	"mail_smtp_password": 'Qidiedu123,.',
	"mail_default_sender": 'info@qidi-edu.com',
	"schedule_edit_timelimit": 2 * 24 * 60 *60,
}

global_vars["mail_messages"] = {
		"password_reset": ["Password reset", "Your password has been reset. \nNew password: %s"], # Difference between these two password resets is that one is done by an administrator and the other is done by requiring the user to use a token from his/her email account
		"reset_email": ["Reset password", "Please go here to reset your password: %s. \nIf you did not request this email you can ignore it."],
		"verify_email": ["Verify email", "Please use this link to verify your email: %s. \nIf you did not request this email you can ignore it."],
		"new_user": ["Your account", "Your username and password has been created in our system. \nPlease use this link to verify your email: %s. \nAfter verifying, log in to change your password: %s"],
		"student_lesson_reminder": ["Upcoming lessons", "You have lessons in two days. You can log in to the website to view you schedule."]
	}

minute = 60
hour = minute * 60
day = hour * 24
week = day * 7

global_vars["database"] = "data_"+global_vars["environment"]+".db"


if global_vars["environment"] == "dev":
	from variables_dev import *
	global_vars["timezone_offset"] = 0
elif global_vars["environment"] == "qidi":
	from variables_qidi import *
	global_vars["timezone_offset"] = -2 * 8 * 60 * 60
elif global_vars["environment"] == "demo":
	from variables_demo import *
	global_vars["timezone_offset"] = -2 * 8 * 60 * 60
else:
	raise custom_exception('Environment not found! ("'+global_vars["environment"] + '")')

	
class_subjects = [
	"Mathematics-Pure math 1",
	"Mathematics-Pure math 2&3",
	"Mathematics-Statistics", 
	"Mathematics-Mechanics",
	"Further Mathematics-Pure math",
	"Further Mathematics-Mechanics",
	"Further Mathematics-Statistics",
	"Physics-AS",
	"Physics-A2",
	"Chemistry-AS",
	"Chemistry-A2",
	"Economics-AS",
	"Economics-A2",
	"Biology-AS",
	"Biology-A2",
	"IELTS-Listening", 
	"IELTS-Speaking",
	"IELTS-Reading",
	"IELTS-Writing",
	"Academic English",
	"Intensive Reading",
]

class_subjects[-1] = "Other"
user_rights = {}

user_rights["admin"] = {
	"delete_staff": True,
	"edit_staff": True,
	"view_staff": True,
	"view_staff_contact": True,
	"delete_students": True,
	"edit_credit": True,
	"edit_students": True,
	"view_students": True,
	"view_students_contact": True,
	"delete_classrooms": True,
	"edit_classrooms": True,
	"view_classrooms": True,
	"delete_classes": True,
	"edit_classes": True,
	"view_classes": True,
	"delete_lessons": True,
	"edit_lessons": True,
	"view_lessons": True,
	"view_log": True,
	"view_day_overview": True,
	"view_teaching_hours_overview": True,
	"view_lesson_overview": True,
}

user_rights["organizer"] = {
	"view_staff": True,
	"view_students": True,
	"view_classes": True,
	"view_lessons": True,
	"view_classrooms": True,
	"view_day_overview": True,
}

user_rights["teacher"] = {
	"view_students": True,
	"view_classes": True,
	"edit_own_classes": True,
	"delete_own_classes": True,
	"view_lessons": True,
	"edit_own_lessons": True,
	"delete_own_lessons": True,
	"view_classrooms": True,
}

def format_response(title, input):
	from django.http import HttpResponse
	output = """
	<html>
	<head>
	<script type="text/javascript" src="/static/jquery-1.11.3.min.js"></script>
	<script type="text/javascript" src="/static/jquery-ui-1.11.4.custom/jquery-ui.js"></script>
	<script type="text/javascript" src="/static/jquery-timepicker/jquery.timepicker.js"></script>
	<script type="text/javascript" src="/static/underscore-min.js"></script>
	<script type="text/javascript" src="/static/functions.js"></script>
	<script type="text/javascript" src="/static/js/user_data.js"></script>
	<script type="text/javascript" src="/static/distro/style_"""+global_vars["environment"]+""".js"></script>
	<link rel="stylesheet" type="text/css" href="/static/jquery-ui-1.11.4.custom/jquery-ui.css">
	<link rel="stylesheet" type="text/css" href="/static/jquery-timepicker/jquery.timepicker.css" />
	<link rel="stylesheet" type="text/css" href="/static/bootstrap/css/bootstrap.min.css" />
	<link rel="stylesheet" type="text/css" href="/static/style.css" />
	<link rel="stylesheet" type="text/css" href="/static/distro/style_"""+global_vars["environment"]+""".css" />
	<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">
	</head>
	<body>
	<div class="header">
		<img src="/static/distro/logo_"""+global_vars["environment"]+""".png" class="logo">
		<div style="text-align: right;"><p style="color: white; padding: 15px; margin: 0px;">"""+(r.session["user"]["name"] if r.session["user"]["logged_in"] else "")+"""&nbsp;&nbsp;&nbsp;&nbsp;<a class="languageSelect """+("languageSelectActive" if r.session['language']=='zh_Hans' else "")+""" " href="#" onclick="post_request({'select_language': 'zh_Hans'});">中文</a> <a class="languageSelect """+("languageSelectActive" if r.session['language']=='en' else "")+""" " href="#" onclick="post_request({'select_language': 'en'});">EN</a></p></div>
	</div>
	<div id="alert_container"></div>
    """
    
	output += """    
	<div class="container-fluid"><div class="row mainrow">
    """
    
	output += '<div class="col-xs-12 col-md-10 col-md-push-2"><div class="row pagebackground"><div class="col-xs-12 titlebox"><h2 class="titleh2">'+title+'</h2></div><div class="col-xs-12 whitepanel animated fadeInUp">'+input+'</div></div></div>'    
    
	output += """<div class="col-xs-12 col-md-2 col-md-pull-10 sidebar">
	"""
	
	admin_links = (
    '<h3>'+t('Administrator')+'</h3>'
	'<a href="/office/staff-overview/">'+version["icons"]["staff"]+t('Staff')+'</a>'
	'<a href="/office/student-overview/">'+version["icons"]["students"]+t('Students')+'</a>'
	'<a href="/office/class-overview/">'+version["icons"]["classes"]+t('Classes')+'</a>'
	'<a href="/office/classroom-overview/">'+version["icons"]["classrooms"]+t('Classrooms')+'</a>'
	'<a href="/office/teaching-hours-overview/">'+version["icons"]["tho"]+t('Monthly Hours')+'</a>'
	'<a href="/office/day-overview/">'+version["icons"]["do"]+t('Classroom View')+'</a>'
	'<a href="/office/log/">'+version["icons"]["log"]+t('Log')+'</a>'
	)
    
	organizer_links = (
	'<h3>'+t('Organizer')+'</h3>'
	'<a href="/office/student-overview/">'+version["icons"]["students"]+t('Students')+'</a> '
	'<a href="/office/class-overview/">'+version["icons"]["classes"]+t('Classes')+'</a> '
	'<a href="/office/classroom-overview/">'+version["icons"]["classrooms"]+t('Classrooms')+'</a> '
	'<a href="/office/day-overview/">'+version["icons"]["do"]+t('Classroom View')+'</a>'
	)
	
	teacher_links = (
	'<h3>'+t('Teacher')+'</h3>'
	'<a href="/office/teacher-default-availability/'+str(r.session["user"]["id"])+'/">'+version["icons"]["devfav"]+t('Default Availability')+'</a>'
	'<a href="/office/staff-schedule/'+str(r.session["user"]["id"])+'/">'+version["icons"]["schedule"]+t('Schedule')+'</a>'
	)    
    
	general_staff_links = (
    '<h3>'+t('Account')+'</h3>'
	'<a href="/users/">'+version["icons"]["notifications"]+t('My Notifications')+'</a> '
	'<a href="/office/staff-edit/'+str(r.session["user"]["id"])+'/">'+version["icons"]["editprof"]+t('Edit Profile')+'</a> '
	'<a href="/users/log-out/">'+version["icons"]["logout"]+t('Log Out')+'</a>'
	)    
	
	student_links = (
	'<h3>'+t('General')+'</h3>'
	'<a href="/office/student-view/'+str(r.session["user"]["id"])+'/">'+t('My Credit')+'</a>'
	'<a href="/office/student-schedule/'+str(r.session["user"]["id"])+'/">'+t('Schedule')+'</a>'
	'<a href="/students/feedback/'+str(r.session["user"]["id"])+'/">'+t('Teacher feedback')+'</a>'
	'<br>'
    '<h3>'+t('Account')+'</h3>'
	'<a href="/users/">'+version["icons"]["notifications"]+t('My notifications')+'</a> '
	'<a href="/office/student-edit/'+str(r.session["user"]["id"])+'/">'+version["icons"]["editprof"]+t('Edit Profile')+'</a> '
	'<a href="/users/log-out/">'+version["icons"]["logout"]+t('Log Out')+'</a>'
	)    

	parent_links = (
    '<h3>'+t('Account')+'</h3>'
	'<a href="/office/parent-edit/'+str(r.session["user"]["id"])+'/">'+version["icons"]["editprof"]+t('Edit Profile')+'</a> '
	'<a href="/users/log-out/">'+version["icons"]["logout"]+t('Log Out')+'</a>'
	)

	logged_out_links = (
		'<h3>'+t('General')+'</h3>'
		'<a href="/users/">'+version["icons"]["login"]+t('Log in')+'</a> '
	)
	
	if r.session["user"]["logged_in"]:
		if r.session["user"]["type"] == 0:
			if r.session["user"]["administrator"]:
				output += admin_links
				for key, right in user_rights["admin"].iteritems():
					r.session["rights"][key] = (r.session["rights"][key] or right)
				
			if r.session["user"]["organizer"]:
				output += organizer_links
				for key, right in user_rights["organizer"].iteritems():
					r.session["rights"][key] = (r.session["rights"][key] or right)

			if r.session["user"]["teacher"]:
				output += teacher_links
				for key, right in user_rights["teacher"].iteritems():
					r.session["rights"][key] = (r.session["rights"][key] or right)
			output += general_staff_links
		elif r.session["user"]["type"] == 1:
			output += student_links
		elif r.session["user"]["type"] == 2:
			output += parent_links

	else:
		output += logged_out_links
	
	output += '</div>'
    
	output += '</div></div></body></html>'
	
	return HttpResponse(output)

#############
# Functions #
#############

def initialize(request):
	global r
	
	r = request
	
	# Set default encoding
	reload(sys)
	sys.setdefaultencoding('UTF8')
	
	if 'user' not in r.session.keys():
		log_out()
	
	r.session.set_expiry(0)

	# Set defaults
	r.session['language'] = r.session.get('language', 'zh_Hans')
	r.session['show_disabled'] = r.session.get('show_disabled', True)
	r.session['last_activity'] = r.session.get('last_activity', 0)
	r.session['schedule_granularity'] = r.session.get('schedule_granularity', 30 * minute)
	r.session['schedule_show_start'] = r.session.get('schedule_show_start', 9.5 * hour)
	r.session['schedule_show_end'] = r.session.get('schedule_show_end', 20.5 * hour)
	r.session['schedule_nudge'] = r.session.get('schedule_nudge', -0.5 * hour)
	r.session['schedule_week'] = r.session.get('schedule_week', int(time.time() / week))
	r.session['schedule_day'] = r.session.get('schedule_day', int(time.time() / day))
	
	if r.session['last_activity'] + hour * 6 < time.time():
		log_out()

	r.session['language'] = r.POST.get("select_language", r.session['language'])
	
	global_vars["schedule_states"] = [t("Unavailable", (), True), t("Available (confirm)", (), True), t("Available", (), True), t("Lesson", (), True), t("Unconfirmed lesson", (), True), t("Teacher busy", (), True)]

	r.session['last_activity'] = time.time()
	
	if r.session["user"]["logged_in"]:
		# Set posts
		r.session['schedule_granularity'] = int(r.POST.get('granularity', r.session['schedule_granularity']))
		r.session['show_disabled'] = bool(int(r.POST.get('show_disabled', r.session['show_disabled'])))

		if 'show_start' in r.POST.keys():
			test = time_to_stamp(r.POST["show_start"], minute * 15)
			if test != False:
				r.session['schedule_show_start'] = test
				r.session['schedule_nudge'] = (r.session['schedule_show_start'] % r.session['schedule_granularity'])
				r.session['schedule_nudge'] = (r.session['schedule_nudge'] - r.session['schedule_granularity'] if r.session['schedule_nudge'] > 0 else 0)
				
		if 'show_end' in r.POST.keys():
			test = time_to_stamp(r.POST["show_end"], minute * 15)
			if test != False:
				r.session['schedule_show_end'] = test

def reset_user_password(type, id, email):
	sql_connection = sqlite3.connect(global_vars["database"])
	sql_cursor = sql_connection.cursor()    
	
	password = random_hex()[:8]
	salt = random_hex()
	pass_hash = pass_derivation(password, salt)
	
	sql_cursor.execute("update "+["staff", "students", "parents"][type]+" set pass_hash = ?, pass_salt = ? where id = ?", (pass_hash, salt, id))
	sql_connection.commit()
	
	result = '<p class="bg-success">'+t('New Password:')+' <b>'+password+'</b></p>'
	if(global_vars["environment"] != "dev"):
		if not send_email(email, t(global_vars["mail_messages"]["password_reset"][0], (), True), t(global_vars["mail_messages"]["password_reset"][1], (password), True)):
			result += '<p class="error_message">'+t('Error sending email to user!')+'</p>'
	
	return result
	
def send_email(to, subject, message):
	import smtplib

	from email.mime.multipart import MIMEMultipart
	from email.mime.text import MIMEText
	from email_template import *
	
	msg = MIMEMultipart('alternative')
	msg['Subject'] = subject
	msg['From'] = global_vars["mail_default_sender"]
	msg['To'] = to
	text = message
	part1 = MIMEText(text, 'plain', "utf-8")
	part2 = MIMEText(html % (subject, global_vars["url"], global_vars["url"], subject, message.replace('\n', '<br />'), global_vars["url"]+"/users/"), 'html', "utf-8")
	msg.attach(part1)
	msg.attach(part2)
	try:
		s = smtplib.SMTP(global_vars["mail_smtp_host"], timeout=3)
		s.set_debuglevel(1)
		s.login(global_vars["mail_smtp_user"], global_vars["mail_smtp_password"])
		s.sendmail(global_vars["mail_default_sender"], to, msg.as_string())
		s.quit()
		return True
	except:
		return False

def escape_xss(input): # TODO
	return input
		
##################
# TIME FUNCTIONS #
##################

def get_weekday(day):
	days = [t("Monday", (), True), t("Tuesday", (), True), t("Wednesday", (), True), t("Thursday", (), True), t("Friday", (), True), t("Saturday", (), True), t("Sunday", (), True)]
	return days[day % 7]

def day_to_stamp(day_number):
	return day_number * day + global_vars["residual_day"] - global_vars["timezone_offset"]

def week_to_stamp(week_number):
	return week_number * week + global_vars["residual_week"] - global_vars["timezone_offset"]

def stamp_to_week(stamp):
	return int((stamp - global_vars["residual_week"] + global_vars["timezone_offset"])/ week)

def time_to_stamp(time, granularity = 1):
	try:
		temp = time.split(":")
		return ((int(temp[0]) * hour + int(temp[1]) * minute) / granularity) * granularity
	except:
		return False

def month_to_stamp(month):
	return time.mktime((int(month/12)+1970, month%12+1, 1, 0, 0, 0, 0, 0, 0))

def stamp_to_display(stamp, format):
	if r.session['language'] == "en":
		formats = {
			"month_time_format": "%B %Y",
			"day_time_format": "%A %B %d, %Y",
			"date_time_format": "%B %d, %Y",
			"time_format": "%B %d, %Y %H:%M:%S",
			}
	
	elif r.session['language'] == "zh_Hans":
		formats = {
			"month_time_format": "%Y年%m月",
			"day_time_format": "%Y年%m月%d日 %A",
			"date_time_format": "%Y年%m月%d日",
			"time_format": "%Y年%m月%d日 %H:%M:%S",
			}
	
	string = datetime.datetime.fromtimestamp(stamp).strftime(formats[format])
	mapping = { 
		'Monday': t('Monday', (), True),
		'Tuesday': t('Tuesday', (), True),
		'Wednesday': t('Wednesday', (), True),
		'Thursday': t('Thursday', (), True),
		'Friday': t('Friday', (), True),
		'Saturday': t('Saturday', (), True),
		'Sunday': t('Sunday', (), True),
		'January': t('January', (), True),
		'February': t('February', (), True),
		'March': t('March', (), True),
		'April': t('April', (), True),
		'May': t('May', (), True),
		'June': t('June', (), True),
		'July': t('July', (), True),
		'August': t('August', (), True),
		'September': t('September', (), True),
		'October': t('October', (), True),
		'November': t('November', (), True),
		'December': t('December', (), True),
	}
	for k, v in mapping.iteritems():
		string = string.replace(k, v)	
	return string
		
def stamp_to_time(stamp):
	minutes = stamp / minute
	return "%02d" % (int(minutes/60))+":"+"%02d" % (minutes%minute)
	
def stamp_to_time_amount(stamp):
	if stamp == 0:
		return t("None", (), True)
	hours = int(stamp/hour)
	minutes = stamp / minute % 60
	if hours > 1:
		if minutes > 0:
			return t("%s hours, %s minutes", (str(hours), str(minutes)), True)
		else: 
			return t("%s hours", (str(hours)), True)
	elif hours == 1:
		if minutes > 0:
			return t("1 hour, %s minutes", (str(minutes)), True)
		else:
			return t("1 hour", (), True)
	else:
		return t("%s minutes", (str(minutes)), True)
	
def to_int(input):
	try:
		return int(input)
	except:
		return 0

def to_str(input):
	if input == None:
		return ""
	try:
		return str(input)
	except:
		return input

##################
# USER FUNCTIONS #
##################

def notify_class_change(sql_cursor, id, lesson = False):
	students = []
	if lesson:
		result = sql_cursor.execute("""SELECT class_id from lessons where id = ?""", (id,)).fetchone()
		id = result[0]
	db_rows = sql_cursor.execute("""select student_id from student_classes where class_id = ?""", (id,))
	for row in db_rows:
		students.append(row[0])
	
	for student_id in students:
		if not (r.session["user"]["type"] == 1 and student_id == r.session["user"]["id"]):
			sql_cursor.execute("""update students set schedule_changed = 1 where id = ?""", (student_id,))

def display_name(first_name, middle_name, last_name):
	m = re.search('(\A[A-Za-z])', first_name)
	if m != None:
		return first_name+" "+last_name
	return last_name+first_name
	
def log_in(email, password, user_type):
	sql_connection = sqlite3.connect(global_vars["database"])
	sql_cursor = sql_connection.cursor()    
	
	# Return values: 0 = logged in, 1 = wrong credentials, 2 = needs to verify
	if user_type == 0:
		result = sql_cursor.execute("""SELECT id, first_name, middle_name, last_name, email, pass_hash, pass_salt, email_verified, administrator, organizer, teacher FROM staff where email = ? and active = 1""", (email,)).fetchone()
		if result == None:
			return 1
		else:
			id, first_name, middle_name, last_name, email, pass_hash, pass_salt, email_verified, administrator, organizer, teacher = result
			if global_vars["environment"] != "dev":
				if not (pass_derivation(password, pass_salt) == pass_hash):
					return 1
				if email_verified != 1:
					return 2
		
		r.session["user"] = {"type": 0, "name": display_name(first_name, middle_name, last_name), "logged_in": True, "id": int(id), "administrator": bool(administrator), "organizer": bool(organizer), "teacher": bool(teacher)}
		return 0
		
	else:
		result = sql_cursor.execute("SELECT id, first_name, middle_name, last_name, email, pass_hash, pass_salt, email_verified FROM students where email = ? and active = 1", (email, )).fetchone()
		if result == None:
			return 1
		else:
			id, first_name, middle_name, last_name, email, pass_hash, pass_salt, email_verified = result
			if global_vars["environment"] != "dev":
				if not (pass_derivation(password, pass_salt) == pass_hash):
					return 1
				if email_verified != 1:
					return 2
		
		r.session["user"] = {"type": user_type, "name": display_name(first_name, middle_name, last_name), "logged_in": True, "id": int(id), "administrator": False, "organizer": False, "teacher": False}
		return 0

def log_message(sql_cursor, message):
	sql_cursor.execute("""INSERT INTO log ('staff_id', 'time', 'message') VALUES (?,?,?)""",(r.session["user"]["id"], time.time(), message))
		
def notify(sql_cursor, user_id, user_type, subject, text):
	sql_cursor.execute("""INSERT INTO notifications ('user_id', 'user_type', 'subject', 'text', 'time') VALUES (?,?,?,?,?)""",(user_id, user_type, subject, text, time.time()))
		
def log_out():
	r.session["user"] = {"logged_in": False, "id": -1, "administrator": 0, "organizer": 0, "teacher": 0}
	r.session["rights"] = {
		"delete_staff": False,
		"edit_staff": False,
		"view_staff": False,
		"view_staff_contact": False,
		"edit_credit": False,

		"delete_students": False,
		"edit_students": False,
		"view_students": False,
		"view_students_contact": False,

		"delete_classrooms": False,
		"edit_classrooms": False,
		"view_classrooms": False,

		"delete_classes": False,
		"edit_classes": False,
		"view_classes": False,
		"delete_lessons": False,

		"edit_lessons": False,
		"view_lessons": False,

		"view_log": False,
		"view_day_overview": False,
		"view_teaching_hours_overview": False,
		"view_lesson_overview": False,
		"edit_own_classes": False,
		"delete_own_classes": False,
		"edit_own_lessons": False,
		"delete_own_lessons": False,
		
	}

def test_password(email, password, type):
	sql_connection = sqlite3.connect(global_vars["database"])
	sql_cursor = sql_connection.cursor()
	if type == 0:
		pass_hash, pass_salt = sql_cursor.execute("""SELECT pass_hash, pass_salt FROM staff where email = ?""", (email,)).fetchone()
	elif type == 1:
		pass_hash, pass_salt = sql_cursor.execute("""SELECT pass_hash, pass_salt FROM students where email = ?""", (email,)).fetchone()
	elif type == 2:
		pass_hash, pass_salt = sql_cursor.execute("""SELECT pass_hash, pass_salt FROM parents where email = ?""", (email,)).fetchone()
	
	return (pass_derivation(password, pass_salt) == pass_hash)
	
def pass_derivation(password, salt):
	h = hashlib.new('ripemd160')
	h.update(password + to_str(salt))
	dk = h.hexdigest()
	h = hashlib.new('sha1')
	h.update(dk + to_str(salt))
	dk = h.hexdigest()
	i = 0
	h = hashlib.new('sha256')
	while i < 100000:
		h.update(dk + to_str(salt))
		dk = h.hexdigest()
		i += 1
	return binascii.hexlify(dk)

def random_hex():
	return hashlib.sha256(str(random.random())).hexdigest()
		
# Lesson
def test_lesson_slot(class_id, start_time, end_time, room_id = -1, ignore_lesson = -1):
	sql_connection = sqlite3.connect(global_vars["database"])
	sql_cursor = sql_connection.cursor()
	
	return_value = {"success": True, "room_booked": True, "message": ""}
	
	if start_time >= end_time:
		return_value["message"] += t("Lesson cannot have no or negative length.")
		return_value["success"] = False
		return return_value
	
	if room_id != -1:
		variables = (ignore_lesson, room_id, start_time, start_time, end_time, end_time, start_time, end_time)
		result = sql_cursor.execute("""select id from lessons where id != ? and (room_id = ? and ((start_time <= ? and end_time > ?) or (start_time < ? and end_time >= ?) or (start_time > ? and end_time <= ?)))""", variables).fetchone()
		if result != None:
			return_value["message"] += t("Room is occupied at this time. ")
			return_value["room_booked"] = False
			room_id = -1
	
	variables = (ignore_lesson, class_id, start_time, start_time, end_time, end_time, start_time, end_time)
	result = sql_cursor.execute("""select id from lessons where id != ? and (((select teacher_id from classes where id = ?) = (select teacher_id from classes where classes.id = lessons.class_id)) and ((start_time <= ? and end_time > ?) or (start_time < ? and end_time >= ?) or (start_time > ? and end_time <= ?)))""", variables).fetchone()
	if result != None:
		return_value["message"] += t("The teacher is busy at this time. ")
		return_value["success"] = False
		return return_value

	students = {}
	db_rows = sql_cursor.execute("""select student_id from student_classes where class_id = ?""", (class_id,))
	for row in db_rows:
		students[row[0]] = students
	
	classes = {}
	
	for student in students:
		db_rows = sql_cursor.execute("""select distinct class_id from student_classes where ? = student_id""", (student,))
		for row in db_rows:
			classes[row[0]] = row[0]

	for student_class in classes:
		variables = (ignore_lesson, student_class, start_time, start_time, end_time, end_time, start_time, end_time)
		result = sql_cursor.execute("""select id from lessons where id != ? and (class_id = ? and ((start_time <= ? and end_time > ?) or (start_time < ? and end_time >= ?) or (start_time > ? and end_time <= ?)))""", variables).fetchone()
		if result != None:
			return_value["message"] += t("One or more students are having class at this time. ")
			return_value["success"] = False
	
	return return_value

def schedule_edit(staff_id = -1):
	
	# AT SOME POINT
	# Do a check to see if submitted schedule is different from present schedule, if not ignore changes, else notify_class_change
	
	output = ""
	# Confirm lessons
	if ('confirm' in r.POST.keys()):
		lesson_id = int(r.POST['confirm'])
		sql_connection = sqlite3.connect(global_vars["database"])
		sql_cursor = sql_connection.cursor()    
		if(r.session["user"]["type"] == 0 and r.session["user"]["id"] == staff_id) or r.session["rights"]["edit_staff"]:
			sql_cursor.execute("""delete from pending_confirmations where lesson_id = ?""", (lesson_id,))
			sql_cursor.execute("""update lessons set confirmed = 1 where id = ?""", (lesson_id,))
			sql_connection.commit()
			notify_class_change(sql_cursor, lesson_id, True)
		elif r.session["user"]["type"] == 1:
			sql_cursor.execute("""delete from pending_confirmations where lesson_id = ? and pending_confirmer_id = ? and pending_confirmer_type = 1""", (lesson_id, r.session["user"]["id"]))
		sql_connection.commit()
		result = sql_cursor.execute("""select id from pending_confirmations where lesson_id = ?""", (lesson_id,)).fetchone()
		if result == None:
			sql_cursor.execute("""Update lessons set confirmed = 1 where id = ?""", (lesson_id,))
			sql_connection.commit()
			notify_class_change(sql_cursor, lesson_id, True)
			
	if 'from_time' in r.POST.keys() and 'schedule' in r.POST.keys() and 'lessons' in r.POST.keys() and 'edit_type' in r.POST.keys():
		staff_id = int(staff_id)
		schedule = json.loads(r.POST['schedule'])
		lessons = json.loads(r.POST['lessons'])
		from_time = int(r.POST["from_time"])
		edit_type = int(r.POST["edit_type"])
		
		sql_connection = sqlite3.connect(global_vars["database"])
		sql_cursor = sql_connection.cursor()    

		
		if edit_type == 1:
			sql_cursor.execute("""delete from teacher_default_schedule where teacher_id = ?""", (staff_id,))
			for key in schedule["0"]:
				sql_cursor.execute("""insert into teacher_default_schedule (teacher_id, status, start_time, end_time) values (?,?,?,?)""", (staff_id, schedule["0"][key]["type"], int(schedule["0"][key]["start_time"]), int(schedule["0"][key]["end_time"])))
			sql_connection.commit()
		
		if edit_type == 2 or edit_type == 3:
			sql_cursor.execute("""delete from teacher_schedule where teacher_id = ? and start_time > ? and end_time < ?""", (staff_id, from_time, from_time + 7 * 24 * 60 * 60))
			sql_connection.commit()
			for key in schedule["1"]:
				if int(key) < 0:
					sql_cursor.execute("""insert into teacher_schedule (teacher_id, status, start_time, end_time) values (?,?,?,?)""", (staff_id, schedule["1"][key]["type"], from_time + int(schedule["1"][key]["start_time"]), from_time + int(schedule["1"][key]["end_time"])))
				else:
					try: # There might still be some BS times around midnight, remove this one day.
						sql_cursor.execute("""insert into teacher_schedule (id, teacher_id, status, start_time, end_time) values (?,?,?,?,?)""", (key, staff_id, schedule["1"][key]["type"], from_time + int(schedule["1"][key]["start_time"]), from_time + int(schedule["1"][key]["end_time"])))
					except:
						pass
			sql_connection.commit()

		if edit_type == 2:
			classes = []
			deleted_lessons = {}
			db_rows = sql_cursor.execute("""SELECT id FROM classes where teacher_id = ?""", (staff_id,))
			for row in db_rows:
				classes.append(row[0])
			for class_id in classes:
				values = (class_id, from_time, from_time + 7 * 24 * 60 * 60)
				db_rows = sql_cursor.execute("""SELECT id, class_id, start_time, end_time FROM lessons where class_id = ? and start_time > ? and end_time < ?""", values)
				for id, class_id, start_time, end_time in db_rows:
					deleted_lessons[id] = {"class_id": class_id, "start_time": start_time, "end_time": end_time}
				sql_cursor.execute("""delete from lessons where class_id = ? and start_time > ? and end_time < ?""", values)
			
			sql_connection.commit()
			notified_classes = []
			for key in schedule["2"]:
				i = 0
				if key in lessons and lessons[key]["class_id"] != -1:
					if not int(lessons[key]["class_id"]) in notified_classes:
						notify_class_change(sql_cursor, lessons[key]["class_id"])
						notified_classes.append(int(lessons[key]["class_id"]))
					repeat = min(int(lessons[key].get('repeat', 1)), 10)
					while i < repeat:
						start_time = from_time + int(schedule["2"][key]["start_time"]) + i * 7 * 24 * 60 * 60
						end_time = from_time + int(schedule["2"][key]["end_time"]) + i * 7 * 24 * 60 * 60
						result = test_lesson_slot(lessons[key]["class_id"], start_time, end_time, lessons[key]["room_id"])
						if result["room_booked"] == False:
							lessons[key]["room_id"] = -1
							output += '<p class="error_message">'+result["message"]+'</p>'
						if result["success"]:
							id = int(key)
							confirmed = (1 if int(schedule["2"][key]["type"]) == 3 else 0)
							if int(key) < 0 or i > 0:
								sql_cursor.execute("""insert into lessons (class_id, room_id, confirmed, start_time, end_time) values (?,?,?,?,?)""", (lessons[key]["class_id"], lessons[key]["room_id"], confirmed, start_time, end_time))
							else: # reuse ID
								deleted_lessons.pop(id, None)
								sql_cursor.execute("""insert into lessons (id, class_id, room_id, confirmed, start_time, end_time) values (?,?,?,?,?,?)""", (id, lessons[key]["class_id"], lessons[key]["room_id"], confirmed, start_time, end_time))
						else:
							output += '<p class="error_message">'+result["message"]+'</p>'
						i += 1
			sql_connection.commit()
			for id, values in deleted_lessons.iteritems():
				log_message(sql_cursor, t("Lesson deleted (Start time: %s, Duration: %s minutes, %s)", (stamp_to_display(values["start_time"], "time_format"), stamp_to_time_amount(values["end_time"]-values["start_time"]), "<a href='/office/class-schedule/"+str(values["class_id"])+"/'>"+t("Class")+"</a>")))
				sql_cursor.execute("""delete from pending_confirmations where lesson_id = ?""", (id,))
				sql_cursor.execute("""delete from student_feedback where lesson_id = ?""", (id,))
			sql_connection.commit()

		if edit_type == 4:
			classes = []
			deleted_lessons = {}
			db_rows = sql_cursor.execute("""SELECT class_id FROM student_classes where student_id = ?""", (r.session["user"]["id"],))
			for row in db_rows:
				classes.append(row[0])
			for class_id in classes:
				values = (class_id, from_time, from_time + 7 * 24 * 60 * 60)
				db_rows = sql_cursor.execute("""SELECT id, class_id, start_time, end_time, (select teacher_id from classes where id = class_id) FROM lessons where class_id = ? and start_time > ? and end_time < ?""", values)
				for id, class_id, start_time, end_time, teacher_id in db_rows:
					deleted_lessons[id] = {"class_id": class_id, "start_time": start_time, "end_time": end_time, "teacher_id": teacher_id}
				sql_cursor.execute("""delete from lessons where class_id = ? and start_time > ? and end_time < ?""", values)
			sql_connection.commit()
			
			notified_classes = []
			for key in schedule["2"]:
				if not key in lessons:
					continue
				start_time = int(schedule["2"][key]["start_time"])
				end_time = int(schedule["2"][key]["end_time"])
				result = test_lesson_slot(lessons[key]["class_id"], start_time, end_time)
				if result["success"]:
					id = int(key)
					# Reuse id?
					if id >= 0:
						deleted_lessons.pop(id, None)
						start_time += from_time
						end_time += from_time
						sql_cursor.execute("""insert into lessons (id, class_id, room_id, confirmed, start_time, end_time) values (?,?,?,?,?,?)""", (id, lessons[key]["class_id"], lessons[key]["room_id"], (1 if int(schedule["2"][key]["type"]) == 3 else 0), start_time, end_time))
						sql_connection.commit()
						
					else:
						if lessons[key]["class_id"] != -1:
							if not int(lessons[key]["class_id"]) in notified_classes:
								notify_class_change(sql_cursor, lessons[key]["class_id"])
								notified_classes.append(int(lessons[key]["class_id"]))
						start_type = 0
						end_type = 0
						result = sql_cursor.execute("""select teacher_id from classes where id = ?""", (lessons[key]["class_id"], )).fetchone()
						teacher_id = int(result[0])
						db_rows = sql_cursor.execute("""SELECT start_time, end_time, status FROM teacher_default_schedule where teacher_id = ?""", (teacher_id,))
						for sch_start_time, sch_end_time, type in db_rows:
							if(sch_start_time <= start_time and sch_end_time >= start_time):
								start_type = type
							if(sch_start_time <= end_time and sch_end_time >= end_time):
								end_type = type

						start_time += from_time
						end_time += from_time
						db_rows = sql_cursor.execute("""SELECT start_time, end_time, status FROM teacher_schedule where teacher_id = ? and start_time >= ? and end_time <= ?""", (result[0], from_time, from_time+7*24*60*60))
						for sch_start_time, sch_end_time, type in db_rows:
							if(sch_start_time <= start_time and sch_end_time >= start_time):
								start_type = type
							if(sch_start_time <= end_time and sch_end_time >= end_time):
								end_type = type

						teacher_availability = min(start_type, end_type) # 0 not available, 1 confirm, 2 no confirm needed
						if teacher_availability != 0:
							confirmed = 0
							if teacher_availability == 2:
								confirmed = 1
							
							classmates = []
							db_rows = sql_cursor.execute("""SELECT student_id from student_classes where class_id = ? and student_id != ?""", (lessons[key]["class_id"], r.session["user"]["id"]))
							for row in db_rows:
								classmates.append(row[0])
								confirmed = 0
							
							notify(sql_cursor, teacher_id, 0, t('Student %s proposed a new lesson', (r.session["user"]["name"])), (t("Lesson has been automatically confirmed. ") if confirmed == 1 else "")+t('Lesson to start on %s. %s to view.', (stamp_to_display(start_time, "date_time_format"), '<a href="/office/staff-schedule/'+str(teacher_id)+'/'+str(stamp_to_week(start_time))+'/">'+t('Click here')+'</a>')))

							sql_cursor.execute("""insert into lessons (class_id, room_id, confirmed, start_time, end_time) values (?,?,?,?,?)""", (lessons[key]["class_id"], -1, confirmed, start_time, end_time))
							lesson_id = sql_cursor.lastrowid
							if teacher_availability == 1:
								sql_cursor.execute("""insert into pending_confirmations (lesson_id, pending_confirmer_id, pending_confirmer_type) values (?,?,?)""", (lesson_id,teacher_id,0))
							
							for class_mate in classmates:
								sql_cursor.execute("""insert into pending_confirmations (lesson_id, pending_confirmer_id, pending_confirmer_type) values (?,?,?)""", (lesson_id,class_mate,1))
						sql_connection.commit()
				else:
					output += '<p class="error_message">'+result["message"]+'</p>'

			for id, values in deleted_lessons.iteritems():
				sql_cursor.execute("""delete from pending_confirmations where lesson_id = ?""", (id,))
				notify(sql_cursor, values["teacher_id"], 0, t('Student %s has cancelled a lesson', (r.session["user"]["name"])), t('Lesson was to start on %s. %s to view new scedule.', (stamp_to_display(values["start_time"], "date_time_format"), '<a href="/office/staff-schedule/'+str(values["teacher_id"])+'/'+str(stamp_to_week(start_time))+'/">'+t('Click here')+'</a>')))
#				log_message(sql_cursor, "Lesson deleted (Start time: "+stamp_to_display(values["start_time"], "time_format")+", Duration: "+str((values["end_time"]-values["start_time"])/60)+" minutes, <a href='/office/class-schedule/"+str(values["class_id"])+"/'>Class</a>)")
	return output

def schedule_html(edit_type, schedule, week_change_url = None, lessons = {}, rooms = {}, classes = {}, teacher_info = {"id": -1, "name": ""}):
	# edit_type 0 = view only, 1 edit anything but lessons (for default schedule only), 2 edit everything but level 0 (which is default), 3 can only edit (non default) availability, 4 is student edit (only propose and cancel certain lessons)
	# week_change_url is needed if there is to be a week changer included
	# rooms and teacher_info is needed if lessons are to be created

	# Change week if needed for mobile view
	selected_day = int(r.POST.get('selected_day', 0));
	if selected_day < 0:
		r.session['schedule_week'] += -1;
	if selected_day > 6:
		r.session['schedule_week'] += +1;
	selected_day = selected_day % 7;
	
	granularity = r.session['schedule_granularity']
	sql_connection = sqlite3.connect(global_vars["database"])
	sql_cursor = sql_connection.cursor()    
    

	if edit_type == 0: # No edit
		default_level = 0
		default_state = 0

	if edit_type == 1:
		default_level = 0
		default_state = 2
	
	if edit_type == 2:
		default_level = 2
		default_state = 3
	
	if edit_type == 3:
		default_level = 1
		default_state = 2
	
	if edit_type == 4:
		default_level = 2
		default_state = -1 # Can't modify anything in this state
	
	output = '<div style="margin: 10px">'

	weekdays = {}
	weekdays[0] = week_to_stamp(r.session['schedule_week'])
	weekdays[1] = weekdays[0] + 24 * 60 * 60
	weekdays[2] = weekdays[1] + 24 * 60 * 60
	weekdays[3] = weekdays[2] + 24 * 60 * 60
	weekdays[4] = weekdays[3] + 24 * 60 * 60
	weekdays[5] = weekdays[4] + 24 * 60 * 60
	weekdays[6] = weekdays[5] + 24 * 60 * 60
	
	output += '<div id="day_changer"></div>'
	
	if edit_type == 4:
		output += '<br /><p style="text-align: center;">'+t('For cancellation of lessons that are within 48 hours, please call the administrator at %s.', ('13162112208'))+'</p>'
	
	time_from = 9 * 60 * 60
	time_to = 21 * 60 * 60
	granularity = 30 * 60
	px_per_gran = 34
	
	total_time = time_to - time_from
	time_rows = int(math.ceil(total_time / granularity))
	total_px_height = px_per_gran * time_rows
	time_per_px = total_px_height / float(total_time)
	
	if edit_type == 2:
		output += (
		'<div style="display:none;">'
		'<div id="lesson_edit" title="'+t("Edit lesson")+'">'
		'<table class="table newuser">'
		'<tr><td>'+t('Class:')+'</td><td><select id="lesson_class">'
		)

		for class_id, value in classes.iteritems():
			if value["can_edit"] > 0 and value['active'] == 1:
				output += '<option value="'+str(class_id)+'">'+value["description"]+'</option>'
		
		output += (
		'</select></td></tr>'
		'<tr><td style="min-width: 110px;">'+t('Pick room:')+'</td><td><div id="lesson_select_holder" style="display: inline;"> </div></td></tr>'
		'<tr><td>'+t('Repeat:%s', (' </td><td><input id="lesson_repeat" style="width: 50px;" value="1">'+t(' time(s)')+'</td>'))+'</tr>'
		'<tr><td></td><td><p class="btn btn-primary" role="button" onclick="close_lesson_edit(true, true)">'+t('Update')+'</p></td></tr>'
        '</table>'
		'<div class="lesson_note"></div>'
		'</div>'
		'</div>')
	
	if edit_type == 2 or edit_type == 3:
		output += (
		'<div style="display:none;">'
		'<div id="room_edit" title="Room lesson">'
		'<p>'+t('This lesson is too close to start time and can no longer be edited.')+'</p><br />'
		'<table class="table newuser">'
		'<tr><td style="min-width: 110px;">Pick room: </td><td><div id="room_select_holder" style="display: inline;"> </div></td></tr>'
		'<tr><td></td><td><p class="btn btn-primary" role="button" onclick="close_lesson_edit(true, false)">'+t('Update')+'</p></td></tr>'
        '</table>'
		'<div class="lesson_note"></div>'
		'</div>'
		'</div>')
	
	output += '<div id="schedule_html"></div>'
	
	if edit_type == 1 or edit_type == 3:
		output += (
			'<p style="text-align: center;"><br><b>'+t('Select input type:')+'</b></p>'
			'<form style="text-align: center;">'
			'<input name="state" type="radio" onclick="mousedown_selected_level = 0; mousedown_selected_state = 1;"> '+global_vars["schedule_states"][1]+' '
			'<input name="state" type="radio" onclick="mousedown_selected_level = 0; mousedown_selected_state = 2;" checked> '+global_vars["schedule_states"][2]+' '
			'</form>'
		)
	
	if edit_type == 2:
		output += (
			'<p style="text-align: center;"><br><b>'+t('Select input type')+':</b></p>'
			'<form style="text-align: center;">'
			'<input name="state" type="radio"  id="schedule_0" onclick="mousedown_selected_level = 1; mousedown_selected_state = 0;"><label for="schedule_0">&nbsp;'+global_vars["schedule_states"][0]+'  </label>  '
			'<input name="state" type="radio"  id="schedule_1" onclick="mousedown_selected_level = 1; mousedown_selected_state = 1;"><label for="schedule_1">&nbsp;'+global_vars["schedule_states"][1]+'  </label>  '
			'<input name="state" type="radio"  id="schedule_2" onclick="mousedown_selected_level = 1; mousedown_selected_state = 2;"><label for="schedule_2">&nbsp;'+global_vars["schedule_states"][2]+'  </label>  '
			'<input name="state" type="radio"  id="schedule_3" onclick="mousedown_selected_level = 2; mousedown_selected_state = 3;" checked><label for="schedule_3">&nbsp;'+t('Lesson', (), True)+'  </label> '
			'</form>'
		)
	
	if edit_type == 4:
		output += (
			'<p style="text-align: center;"><br><b>'+t('Select class:')+'</b></p>'
			'<table style="width: 400px; margin-left: auto; margin-right: auto;"><form style="text-align: center;">')
		for key, class_values in classes.iteritems():
			if class_values["can_edit"]:
				output += '<tr><td><input name="state" type="radio" onclick="select_layer('+str(class_values["teacher_id"])+', '+str(key)+')"> </td><td>&nbsp;'+class_values["description"]+'</td><td>Credit: <div id="class_credit_'+str(key)+'" style="display: inline;"></div></td></tr>'
		output += """
		</form></table>
			<script>
			function select_layer(layer, class_id){
			selected_layer = layer;
			selected_class = class_id;
			mousedown_selected_state = 4;
			// Unset unsaved lessons
			for (var id in lessons) {
				if(id < 0){ // Id less than 0 means its unsaved
					delete schedule[2][id]; 
					delete lessons[id]; 
				}
			}
			update_schedule();
			}
			</script>
		"""
	
	if edit_type != 0:
		output += (
		'<div id="submit_div" style="display:none;">'
		'<form action="?" method="post" id="schedule_form">'
		'<input type="hidden" name="selected_day" id="selected_day_submit_value" value="0">'
		'<input type="hidden" name="from_time" value="'+str(week_to_stamp(r.session['schedule_week']))+'">'
		'<input type="hidden" name="schedule" id="schedule_submit_value" value="">'
		'<input type="hidden" name="lessons" id="lessons_submit_value" value="">'
		'<input type="hidden" name="edit_type" value="'+str(edit_type)+'">'
		'</form>'
		'<div style="text-align: center;">'
		'<button class="btn btn-primary" style="display: inline; margin: auto;" type="submit" onclick="get_reload();">'+t("Cancel updates")+'</button>  '
		'<button class="btn btn-primary" style="display: inline; margin: auto;" type="submit" onclick="schedule_submit();">'+t("Update")+'</button>'
#		'</div>'
		'</div>'
		'</div>')
	
	output += """
	<script>
	var day_start = """+str(time_from)+""";
	var day_end = """+str(time_to)+""";
	var time_px = """+str(time_per_px)+""";
	var day_length = 60 * 60 * 24;
	var granularity = 15 * 60;
	var edit_type = """+str(edit_type)+""";
	var selected_class = -1;
	
	var schedule = """+json.dumps(schedule)+""";
	var lessons = """+json.dumps(lessons)+""";
	var classes = """+json.dumps(classes)+""";
	var rooms = """+json.dumps(rooms)+""";
	var teacher_info = """+json.dumps(teacher_info)+""";
	var schedule_states = """+json.dumps(global_vars["schedule_states"])+""";
	
	var editing_lesson = -1;
	var selected_layer = -1;
	var edit_enabled = true;
	
	var schedule_edit_timelimit = """+("-1" if r.session["rights"]["edit_staff"] else str(global_vars["schedule_edit_timelimit"]))+""";
	var time_now = """+str(time.time()-week_to_stamp(r.session['schedule_week']))+""";
	
	var selected_day = """+str(selected_day)+""";
	var schedule_top = 0;
	var mousedown = false;
	var mousedown_position = 0;
	var mousedown_offset = 0;
	var mousedown_day = 0;
	var mousedown_selected_level = """+str(default_level)+""";
	var mousedown_selected_level_temp = """+str(default_level)+""";
	var mousedown_selected_state = """+str(default_state)+""";
	var mousedown_selected = 0;
	var mousedown_mode = 0;
	var new_counter = 0;
	
	var spent_credit = {};
	var full_view = (window.innerWidth >= 800);
	
	$(window).on('resize', function(){
		schedule_top = $("#day_div_"+selected_day).offset().top;
		full_view = (window.innerWidth >= 800);
		update_day_changer();
		update_schedule();
	});
	
	function update_day_changer(){
		day_changer_html = "";
		if (full_view){
			schedule_html = '<table class="schedule_table noselect">';"""
	if edit_type != 1:
		output += """
			day_changer_html = '<div class="row">'+
			'<div class="col-xs-4"><a class="btn btn-default center-block" role="button" href=\""""+week_change_url+'/'+str(r.session['schedule_week']-1)+"""/\">"""+t('Previous Week')+"""</a></div>'+
			'<div class="col-xs-4"></div> '+
			'<div class="col-xs-4"><a class="btn btn-default center-block" role="button" href=\""""+week_change_url+'/'+str(r.session['schedule_week']+1)+"""/">"""+t('Next Week')+"""</a></div>'+
			'</div>'
			schedule_html += '<tr>'+
				'<td></td>'+
				'<td><b>"""+get_weekday(0)+"""</b><br />"""+stamp_to_display(weekdays[0], "date_time_format")+"""</td>'+
				'<td><b>"""+get_weekday(1)+"""</b><br />"""+stamp_to_display(weekdays[1], "date_time_format")+"""</td>'+
				'<td><b>"""+get_weekday(2)+"""</b><br />"""+stamp_to_display(weekdays[2], "date_time_format")+"""</td>'+
				'<td><b>"""+get_weekday(3)+"""</b><br />"""+stamp_to_display(weekdays[3], "date_time_format")+"""</td>'+
				'<td><b>"""+get_weekday(4)+"""</b><br />"""+stamp_to_display(weekdays[4], "date_time_format")+"""</td>'+
				'<td><b>"""+get_weekday(5)+"""</b><br />"""+stamp_to_display(weekdays[5], "date_time_format")+"""</td>'+
				'<td><b>"""+get_weekday(6)+"""</b><br />"""+stamp_to_display(weekdays[6], "date_time_format")+"""</td>'+
			'</tr>';"""
		
	output += """
			schedule_html += '<tr>'+
				'<td class="schedule_table_times schedule_table_background">'+"""
	time_loop = time_from
	while time_loop < time_to:
		output += '\'<div style="height: '+str(px_per_gran)+'px;">'+stamp_to_time(time_loop)+'</div>\'+'
		time_loop += granularity
	
	output += '\'</td>\'+'
	
	schedule_day = 0
	while schedule_day < 7:
		output += '\'<td class="schedule_table_background"><div class="schedule_container schedule_draggable" style="height: '+str(total_px_height)+'px;" id="day_div_'+str(schedule_day)+'"> </div></td>\'+'
		schedule_day += 1
	
	output += """
			'</tr>'+
		'</table>'
			
		}else{
			schedule_html = '<table class="schedule_table noselect" style="width: 100%">';"""
	if edit_type != 1:
		output += """
			day_changer_html = '<div class="row">'+
			'<div class="col-xs-4"><a class="btn btn-default center-block" role="button" href="#" onclick="post_request({\\'selected_day\\': '+(selected_day-1)+'}, \\'"""+week_change_url+'/'+str(r.session['schedule_week'])+"""/\\');">"""+t("Previous day")+"""</a></div>'+
			'<div class="col-xs-4"><h4 style="text-align: center;">"""+str(stamp_to_display(weekdays[selected_day], "date_time_format"))+"""</h4></div> '+
			'<div class="col-xs-4"><a class="btn btn-default center-block" role="button" href="#" onclick="post_request({\\'selected_day\\': '+(selected_day+1)+'}, \\'"""+week_change_url+'/'+str(r.session['schedule_week'])+"""/\\');">"""+t("Next day")+"""</a></div>'+
			'</div>'
			schedule_html += '<tr>'+
				'<td colspan="2"><b>"""+get_weekday(selected_day)+"""</b><br />"""+stamp_to_display(weekdays[selected_day], "date_time_format")+"""</td>'+
			'</tr>';"""
	output += """
			schedule_html += '<tr>'+
				'<td class="schedule_table_times schedule_table_background">'+"""
	time_loop = time_from
	while time_loop < time_to:
		output += '\'<div style="height: '+str(px_per_gran)+'px; width: 100px;">'+stamp_to_time(time_loop)+'</div>\'+'
		time_loop += granularity
	
	output += '\'</td><td  class="schedule_table_background" style="width: 100%"><div class="schedule_container schedule_draggable" style="height: '+str(total_px_height)+'px;" id="day_div_'+str(selected_day)+'"> </div></td>\'+'
	
	output += """
				'</tr>'+
			'</table>'
		}
		$("#day_changer").html(day_changer_html);
		$("#schedule_html").html(schedule_html);
		schedule_top = $("#day_div_"+selected_day).offset().top;"""
	output += """
	}
	update_day_changer();

	function schedule_submit(){
		$("#schedule_submit_value").val(JSON.stringify(schedule));
		$("#lessons_submit_value").val(JSON.stringify(lessons));
		$("#selected_day_submit_value").val(selected_day);
		$("#schedule_form").submit();
	}
	
	function time_from_stamp(stamp){
		var pad = "00";
		stamp = stamp % (24 * 60 * 60);

		hours = "" + Math.floor(stamp / (60 * 60));
		hours = pad.substring(0, pad.length - hours.length) + hours;
		minutes = "" +Math.floor((stamp % (60 * 60)/ 60));
		minutes = pad.substring(0, pad.length - minutes.length) + minutes;
		return hours + ":" + minutes;
	}
	
	function room_conflict(start_time, end_time, room_id){
		for (var lesson_id in rooms[room_id]["unavailable"]){
			if(!time_conflict(start_time, end_time, rooms[room_id]["unavailable"][lesson_id]["start_time"], rooms[room_id]["unavailable"][lesson_id]["end_time"])){
				return false;
			}
		}
		return true;
	}
	
	function start_lesson_edit(id, full_edit){
		edit_enabled = false;
		editing_lesson = id;
		
		var available_rooms = {};
		for(var room in rooms){
			available_rooms[room] = {"name": rooms[room]["name"], "available": true};
			
			if(!room_conflict(schedule[2][id]['start_time'], schedule[2][id]['end_time'], room)){
				available_rooms[room]["available"] = false;
			}
		}
		
		var option_string = '<select id="lesson_room">';
		var unavailable_rooms = [];
		for (room in available_rooms){
			if(available_rooms[room]["available"]){
				option_string += '<option value="'+room+'">'+available_rooms[room]["name"]+'</option>';
			}else{
				unavailable_rooms.push(available_rooms[room]["name"]);
			}
		}
		option_string += '<option value="-1">"""+t("Not selected")+"""</option></select>';
		
		if(unavailable_rooms.length > 0){
			$(".lesson_note").html(\""""+t('(The following rooms are unavailable: %%s)')+"""".replace('%s', unavailable_rooms.join(", "))+";");
		}else{
			$(".lesson_note").html("");
		}
		
		$("#lesson_select_holder").html();
		$("#room_select_holder").html();
		if(full_edit){
			$("#lesson_select_holder").html(option_string);
		}else{
			$("#room_select_holder").html(option_string);
		}
		
		$("#lesson_room option[value='"+lessons[id]["room_id"]+"']").prop('selected', true);
		if(full_edit){
			repeat = (typeof lessons[id]["repeat"] === 'undefined' ? 1 : lessons[id]["repeat"]);
			$("#lesson_repeat").val(repeat);
			$("#lesson_class option[value='"+lessons[id]["class_id"]+"']").prop('selected', true);
			
			$(function() {
				$("#lesson_edit").dialog({
					width: 600,
					close: function(event, ui) { close_lesson_edit(false, true) },
				});
			});
		}else{
			$(function() {
				$("#room_edit").dialog({
					width: 600,
					close: function(event, ui) { close_lesson_edit(false, false) },
				});
			});
		
		}
	}
	
	function close_lesson_edit(save, full_edit){
		if(save){
			lessons[editing_lesson]["room_id"] = $("#lesson_room").val();
			if(full_edit){
				lessons[editing_lesson]["class_id"] = $("#lesson_class").val();
				lessons[editing_lesson]["repeat"] = $("#lesson_repeat").val();
			}
		}
		update_schedule();
		if(full_edit){
			$("#lesson_edit").dialog("close");
		}else{
			$("#room_edit").dialog("close");
		}
		edit_enabled = true;
	}
	
	function update_schedule(){
		spent_credit = {};
		day = 0;
		while(day < 7){
			$('#day_div_'+day).html("");
			day += 1;
		}

		for (var level in schedule) {
			for (var id in schedule[level]) {
				if (schedule[level][id]['type'] == 3 || schedule[level][id]['type'] == 4){
					if(id < 0){
						if (typeof spent_credit[lessons[id]["class_id"]] === 'undefined') {
							spent_credit[lessons[id]["class_id"]] = 0;
						}
						spent_credit[lessons[id]["class_id"]] += schedule[level][id]["end_time"] - schedule[level][id]["start_time"];
					}
				}

				// Which day
				var start_day = Math.floor(schedule[level][id]['start_time'] / day_length);
				var end_day = Math.floor(schedule[level][id]['end_time'] / day_length);
				
				day = 0;
				while(end_day - start_day >= day && day + start_day < 7){

					// Make sure it doesn't end before the start of the schedule or starts after the end of the schedule
					if(schedule[level][id]['start_time'] - (start_day + day) * day_length > day_end || schedule[level][id]['end_time'] - (start_day + day) * day_length > day_start){

						// Time relative to that day
						var time_start = Math.max(0, schedule[level][id]['start_time'] - (start_day + day) * day_length - day_start);
						var time_end = Math.min((day_end - day_start) - time_start, schedule[level][id]['end_time'] - time_start - (start_day + day) * day_length - day_start);
						
						time_period_text = time_from_stamp(schedule[level][id]['start_time'])+'-'+time_from_stamp(schedule[level][id]['end_time']);
						
						// Edit user: 0 staff, 1 student
						// Edit levels staff: 0 full rights, 1 we can only edit room, 2 no rights
						// Edit levels user: 0 we created unconfirmed lesson ourself, 1 we can only cancel, 2 no rights
						
						edit_user = (edit_type != 4 ? 0 : 1);
						
						// First assume full edit rights
						edit_level = 0;
						
						// Then check if we can edit this slot and we are not in view "edit_type" mode
						if(schedule[level][id]['editable'] == 0 || edit_type == 0){
							edit_level = 2;
						}
						
						// Did we create the lesson ourself as a student?
						if(edit_user == 1 && edit_level == 0 && id >= 0){
							edit_level = 1;
						}
						
						html = "";
						if (schedule[level][id]['type'] == 3 || schedule[level][id]['type'] == 4){
							if(lessons[id]["class_id"] != -1){
								if(classes[lessons[id]["class_id"]]["can_edit"] == 0){
									edit_level = 2;
								}
								if(schedule_edit_timelimit != -1 && schedule_edit_timelimit + time_now > schedule[level][id]['start_time']){
									// Teachers can edit rooms 
									edit_level = (edit_user == 0 ? 1 : 2);
								}
							}
							if((edit_level == 0 || edit_level == 1) && edit_user == 0 && full_view){
								html = '<p class="schedule_cell_p link" onclick="start_lesson_edit('+id+', '+(edit_level == 0 ? 'true' : 'false')+')">'+time_period_text+'<br />';
							}else{
								html = '<p class="schedule_cell_p">'+time_period_text+'<br />';
							}
							if(lessons[id]["class_id"] != -1){
								if (lessons[id]["room_id"] != -1){
									var room_name = rooms[lessons[id]["room_id"]]["name"];
								}else{
									var room_name = (edit_type == 2?'<b style="color: #ff6130;">"""+t('Not selected')+"""</b>':'"""+t('Not selected')+"""');
								}
								if(schedule[level][id]['type'] == 4){
									html += '<b>Unconfirmed</b><br />';
								}
								html += '"""+t('Teacher: ')+"""'+lessons[id]["teacher_name"]+'<br />'+(schedule[level][id]['type'] != 4 ? '"""+t('Room: ')+"""'+room_name+'<br />' : '') + '"""+t('Students: ')+"""'+classes[lessons[id]["class_id"]]["students"]+'</p>';
							}else if(full_view){
								html += '"""+t('CLICK TO CONFIGURE')+"""</p>';
							}
							
							// We can only confirm if not already confirmed
							can_confirm = lessons[id]["can_confirm"] && schedule[level][id]['type'] == 4;
						
						}else{
							html = '<p class="schedule_cell_p">'+time_period_text+'<br />'+schedule_states[schedule[level][id]['type']]+'</p>';
							can_confirm = false;
						}
						
						if(edit_level == 0){
							if(can_confirm){
								if(full_view){
									edit = "<div class='schedule_delete_div noselect' id='delete_"+id+"'> </div><div class='schedule_confirm_div noselect' id='confirm_"+id+"'></div>";
								}else{
									edit = "<div class='schedule_delete_div_mobile noselect' id='delete_"+id+"'> </div><div class='schedule_confirm_div_mobile noselect' id='confirm_"+id+"'></div>";
								}
							}else{
								if(full_view){
									edit = "<div class='schedule_delete_div noselect' id='delete_"+id+"'> </div><div class='schedule_move_div noselect' id='move_"+id+"'> </div><div class='schedule_resize_div' id='resize_"+id+"'> </div>";
								}else{
									edit = "<div class='schedule_delete_div_mobile noselect' id='delete_"+id+"'> </div><div class='schedule_move_up_div_mobile noselect' id='move_up_"+id+"'> </div><div class='schedule_move_down_div_mobile noselect' id='move_down_"+id+"'> </div><div class='schedule_expand_up_div_mobile noselect' id='expand_up_"+id+"'> </div><div class='schedule_expand_down_div_mobile noselect' id='expand_down_"+id+"'> </div>";
								}
							}
						}else if(edit_level == 1 && edit_user == 1){
							edit = "<div class='schedule_delete_div"+(full_view ? "" : "_mobile")+" noselect' id='delete_"+id+"'> </div>"+(can_confirm ? "<div class='schedule_confirm_div"+(full_view ? "" : "_mobile")+" noselect' id='confirm_"+id+"'> </div>" : "");
						}else{
							edit = "";
						}
						
						if(schedule[level][id]["layer"] == -1 || schedule[level][id]["layer"] == selected_layer){
							$('#day_div_'+(start_day + day)).append("<div id='slot_"+level+"_"+id+"' class='schedule schedule_"+schedule[level][id]['type']+" noselect' style='top: "+(time_start * time_px)+"px;height: "+(time_end * time_px)+"px;'>"+edit+html+"</div>");
						}
					}
					day += 1;
				}
			}
		}
		
		// Update credit counter
		if(edit_type == 4){
			for(var key in classes){
				if (typeof spent_credit[key] === 'undefined') {
					var spent = 0;
				}else{
					spent = spent_credit[key];
				}

				$("#class_credit_"+key).html((classes[key]["credit"] - spent) /3600);
			}
		}
	}

	function div_is_clicked(point_x, point_y, id){
		if (typeof $(id).offset() === 'undefined') {
			return false;
		}

		var left = $(id).offset().left;
		var right = left + $(id).width();
		var top = $(id).offset().top;
		var bottom = top + $(id).height();
		if(left < point_x && right > point_x && top < point_y && bottom > point_y){
			return true;
		}else{
			return false;
		}
	}
	
	function time_conflict (start_0, end_0, start_1, end_1){
		// Starts before and ends after start time
		if(start_0 >= start_1 && start_0 < end_1){
			return false;
		}
		// Starts before and ends after end time
		if(end_0 > start_1 && end_0 <= end_1){
			return false;
		}
		// Also not fully within
		if(start_0 <= start_1 && end_0 >= end_1){
			return false;
		}
		return true;
	}
	
	function move_timeslot(type, level, layer, slot_id, start_time, end_time){
		
		if(!edit_enabled || type == -1){
			return false;
		}

		if(layer != -1){
			var start_time_ok = false;
			var end_time_ok = false;
			for (var id in schedule[0]){
				if(schedule[0][id]["layer"] == layer){
					if(schedule[0][id]["start_time"] <= start_time && schedule[0][id]["end_time"] >= start_time){
						start_time_ok = true;
					}
					if(schedule[0][id]["start_time"] <= end_time && schedule[0][id]["end_time"] >= end_time){
						end_time_ok = true;
					}
				}
			}
			for (var id in schedule[1]){
				if(schedule[1][id]["layer"] == layer){
					if(schedule[1][id]["start_time"] <= start_time && schedule[1][id]["end_time"] >= start_time){
						start_time_ok = true;
					}
					if(schedule[1][id]["start_time"] <= end_time && schedule[1][id]["end_time"] >= end_time){
						end_time_ok = true;
					}
				}
			}
			if(start_time_ok != true || end_time_ok != true){
				return false;
			}
		}

		// Make sure the teacher can actually book at this time
		if((type == 3 || type == 4) && schedule_edit_timelimit != -1 && schedule_edit_timelimit + time_now > start_time){
			alert_message('"""+t("You don\\'t have the rights to edit lessons starting within %%s hours.")+"""'.replace('%s', (schedule_edit_timelimit/3600)), "error");
			return false;
		}
		
		if(end_time - start_time < granularity){
			return false;
		}
		
		for (var id in schedule[level]){
			if(id != slot_id && (schedule[level][id]["layer"] == -1 || schedule[level][id]["layer"] == selected_layer)){
				if(!time_conflict(schedule[level][id]["start_time"], schedule[level][id]["end_time"], start_time, end_time)){
					return false;
				}
			}
		}
		
		return true;
	}
	
	function user_click(click_x, click_y, touch_click){
		if(!edit_enabled || edit_type == 0){
			return false; // Returning false here causes a drop down menu bug
		}
		$( "#submit_div" ).show();

		day = 0;
		while (day < 7){
			// Check if we are clicking inside one of the days
			if (div_is_clicked(click_x, click_y, "#day_div_"+day)){
				mousedown_day = day;
				mousedown = true;
				// Check if we are clicking inside a delete or move icon
				for (var level in schedule) {
					for (var id in schedule[level]) {
						if (div_is_clicked(click_x, click_y, "#move_up_"+id)){
							start_time = Math.max(day_start + mousedown_day * day_length, schedule[level][id]["start_time"] - granularity);
							end_time = Math.min(day_end + mousedown_day * day_length, schedule[level][id]["end_time"] - granularity);
							if(move_timeslot(schedule[level][id]['type'], level, schedule[level][id]['layer'], id, start_time, end_time)){
								schedule[level][id]["start_time"] = start_time;
								schedule[level][id]["end_time"] = end_time;
								update_schedule();
							}
							return true;
							
						}else if (div_is_clicked(click_x, click_y, "#move_down_"+id)){
							start_time = Math.max(day_start + mousedown_day * day_length, schedule[level][id]["start_time"] + granularity);
							end_time = Math.min(day_end + mousedown_day * day_length, schedule[level][id]["end_time"] + granularity);
							if(move_timeslot(schedule[level][id]['type'], level, schedule[level][id]['layer'], id, start_time, end_time)){
								schedule[level][id]["start_time"] = start_time;
								schedule[level][id]["end_time"] = end_time;
								update_schedule();
							}
							return true;
							
						}else if (div_is_clicked(click_x, click_y, "#expand_up_"+id)){
							// We don't need to check a bunch of stuff here since this will make the schedule item smaller only
							schedule[level][id]["end_time"] -= granularity;
							update_schedule();
							return true;

						}else if (div_is_clicked(click_x, click_y, "#expand_down_"+id)){
							end_time = Math.min(day_end + mousedown_day * day_length, schedule[level][id]["end_time"] + granularity);
							if(move_timeslot(schedule[level][id]['type'], level, schedule[level][id]['layer'], id, schedule[level][id]["start_time"], end_time)){
								schedule[level][id]["end_time"] = end_time;
								update_schedule();
							}
							return true;

						}else if (div_is_clicked(click_x, click_y, "#move_"+id)){
							mousedown_mode = 0;
							mousedown_selected_level_temp = mousedown_selected_level;
							mousedown_selected_level = level;
							mousedown_selected = parseInt(id);
							return true;
							
						}else if (div_is_clicked(click_x, click_y, "#resize_"+id)){
							mousedown_mode = 1;
							mousedown_selected_level_temp = mousedown_selected_level;
							mousedown_selected_level = level;
							mousedown_selected = parseInt(id);
							return true;
							
						}else if (div_is_clicked(click_x, click_y, "#delete_"+id)){
							delete schedule[level][id]; 
							update_schedule();
							mousedown = false;
							mousedown_selected = false;
							return true;
							
						}else if (div_is_clicked(click_x, click_y, "#confirm_"+id)){
							post_request({"confirm": id});
							update_schedule();
							mousedown = false;
							mousedown_selected = false;
							return true;
						}
					}
				}
				
				// Make sure we didn't click on a lesson
				for (var id in schedule[2]) {
					if (div_is_clicked(click_x, click_y, "#slot_2_"+id)){
						return false;
					}
				}

				mousedown_position = click_y - schedule_top;
				start_time = Math.floor((mousedown_day * day_length + mousedown_position / time_px + day_start)/granularity)*granularity;
				
				if(mousedown_selected_state == -1 && edit_type == 4){
					alert_message("Please select a class to create lessons.", "info");
				}
				
				if(edit_type == 4 && (mousedown_selected_state == 3 || mousedown_selected_state == 4)){
					if(classes[selected_class]["credit"] <= 0){
						alert_message('"""+t("You do not have enough credit with this class to book lessons.")+"""', "error");
						return false;
					}
				}
				
				new_counter += -1;

				if(touch_click){
					test_length = 4;
				}else{
					test_length = 1;
				}
				
				length_success = false;
				
				while(test_length > 0 && !length_success){
					length_success = true;
					end_time = start_time + granularity * test_length;
					
					length_success

					if(!move_timeslot(mousedown_selected_state, mousedown_selected_level, selected_layer, new_counter, start_time, end_time)){
						length_success = false;
					}
					
					// Check that student has enought credit
					if(edit_type == 4 && (mousedown_selected_state == 3 || mousedown_selected_state == 4)){
						credit_left = classes[selected_class]["credit"];
						for(id in schedule[mousedown_selected_level]){
							// Id must be less than 0 (non saved slot) and of the same class_id
							if (id < 0 && lessons[id]["class_id"] == selected_class){
								credit_left -= schedule[mousedown_selected_level][id]["end_time"] - schedule[mousedown_selected_level][id]["start_time"];
							}
						}
						credit_left -= end_time - start_time;
						if (credit_left < 0){
							length_success = false;
						}
					}
					test_length -= 1;
				}
				
				if(!length_success){
					return false;
				}

				mousedown_selected = new_counter;
				mousedown_mode = 1;
				
				// Create lesson if needed
				if(mousedown_selected_state == 3){
					lessons[new_counter] = {"class_id": selected_class, "teacher_name": teacher_info["name"], "subject_id": -1, "room_id": -1, "id": new_counter, "confirmed": true};
				}
				if(mousedown_selected_state == 4){
					lessons[new_counter] = {"class_id": selected_class, "teacher_name": classes[selected_class]["teacher_name"], "subject_id": -1, "room_id": -1, "id": new_counter, "confirmed": false};
				}
				schedule[mousedown_selected_level][new_counter] = {"start_time": start_time, "type": mousedown_selected_state, "id": new_counter, "end_time": end_time, "layer": -1};
				
				update_schedule();
				return true;
			}
			day += 1;
		}
		return false;
	}
	
	function user_click_stop(e){
		mousedown = false;
		mousedown_mode = -1;
		mousedown_selected_level = mousedown_selected_level_temp;

	}
	
	function user_move(pos_y){
		if(mousedown_mode != -1 && mousedown && typeof schedule[mousedown_selected_level][mousedown_selected] == "object" && typeof schedule[mousedown_selected_level][mousedown_selected]["start_time"] == "number"){
			var mouse_time_position = Math.floor((mousedown_day * day_length + (pos_y - schedule_top) / time_px + day_start)/granularity)*granularity;
			
			start_time = schedule[mousedown_selected_level][mousedown_selected]["start_time"];
			end_time = schedule[mousedown_selected_level][mousedown_selected]["end_time"];
			
			if(mousedown_mode == 0){
				var length = schedule[mousedown_selected_level][mousedown_selected]["end_time"] - schedule[mousedown_selected_level][mousedown_selected]["start_time"];
				start_time = mouse_time_position;
				end_time = mouse_time_position + length;
			}else if(mousedown_mode == 1){
				end_time = mouse_time_position;
			}
			
			start_time = Math.max(day_start + mousedown_day * day_length, start_time);
			end_time = Math.min(day_end + mousedown_day * day_length, end_time);
			
			// Check that student has enought credit
			if(edit_type == 4 && (schedule[mousedown_selected_level][mousedown_selected]['type'] == 3 || schedule[mousedown_selected_level][mousedown_selected]['type'] == 4)){
				credit_left = classes[lessons[mousedown_selected]["class_id"]]["credit"];
				for(id in schedule[mousedown_selected_level]){
					// Id must be less than 0 (non saved slot), not itself (accounted for below) and of the same class_id
					if (id < 0 && id != mousedown_selected && lessons[id]["class_id"] == lessons[mousedown_selected]["class_id"]) {
						credit_left -= schedule[mousedown_selected_level][id]["end_time"] - schedule[mousedown_selected_level][id]["start_time"];
					}
				}
				credit_left -= end_time - start_time;
				if (credit_left < 0){
					return false;
				}
			}
			
			if(move_timeslot(mousedown_selected_state, mousedown_selected_level, selected_layer, mousedown_selected, start_time, end_time)){
				schedule[mousedown_selected_level][mousedown_selected]["start_time"] = start_time;
				schedule[mousedown_selected_level][mousedown_selected]["end_time"] = end_time;
			}
			
			// Unset room if we move to a place where it isn't available
			if(typeof lessons[mousedown_selected] !== 'undefined' && lessons[mousedown_selected]["room_id"] != -1){
				if(!room_conflict(schedule[mousedown_selected_level][mousedown_selected]["start_time"], schedule[mousedown_selected_level][mousedown_selected]["end_time"], lessons[mousedown_selected]["room_id"])){
					lessons[mousedown_selected]["room_id"] = -1;
				}
			}
			update_schedule();
		}
	}
	
	// Check for touch
	if ('ontouchstart' in window || navigator.maxTouchPoints){
		var touch_hold = false;

		document.addEventListener('touchstart', function(e) {
			if(edit_enabled){
				if(user_click(e.touches[0].pageX, e.touches[0].pageY, true)){
					touch_hold = true;
				}
			}		
		}, true);
		
		document.addEventListener('touchmove', function(e) {
			if(touch_hold){
				user_move(e.touches[0].pageY);
				e.preventDefault();
			}
		}, true);
		
		document.addEventListener('touchend', function(e) {
			touch_hold = false;
			user_click_stop(e);
		}, true);
		
		document.addEventListener('touchcancel', function(e) {
			touch_hold = false;
			user_click_stop(e);
		}, true);
	
	}else{
		$(document).mousedown(function(e){
			user_click(e.pageX, e.pageY, false);
		});
		
		$(document).mousemove(function(e){
			user_move(e.pageY);
		});

		$(document).mouseup(function(e) {
			mousedown = false;
			user_click_stop(e);
		});
	}
	
	update_schedule();
	</script></div>
	"""
	return output